<?php

declare(strict_types=1);

/* (c) Anton Medvedev <anton@medv.io>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Deployer\Support;

function array_flatten(array $array): array
{
    $flatten = [];
    array_walk_recursive($array, function ($value) use (&$flatten) {
        $flatten[] = $value;
    });
    return $flatten;
}

/**
 * Recursively merge two config arrays with a specific behavior:
 *
 * 1. scalar values are overridden
 * 2. array values are extended uniquely if all keys are numeric
 * 3. all other array values are merged
 */
function array_merge_alternate(array $original, array $override): array
{
    foreach ($override as $key => $value) {
        if (isset($original[$key])) {
            if (!is_array($original[$key])) {
                if (is_numeric($key)) {
                    // Append scalar value
                    $original[] = $value;
                } else {
                    // Override scalar value
                    $original[$key] = $value;
                }
            } elseif (array_keys($original[$key]) === range(0, count($original[$key]) - 1)) {
                // Uniquely append to array with numeric keys
                $original[$key] = array_unique(array_merge($original[$key], $value));
            } else {
                // Merge all other arrays
                $original[$key] = array_merge_alternate($original[$key], $value);
            }
        } else {
            // Simply add new key/value
            $original[$key] = $value;
        }
    }

    return $original;
}

function env_stringify(array $array): string
{
    return implode(' ', array_map(
        function ($key, $value) {
            return sprintf("%s=%s", $key, escapeshellarg((string) $value));
        },
        array_keys($array),
        $array,
    ));
}

function is_closure(mixed $var): bool
{
    return is_object($var) && ($var instanceof \Closure);
}

/**
 * Check if all elements satisfy predicate.
 */
function array_all(array $array, callable $predicate): bool
{
    foreach ($array as $key => $value) {
        if (!$predicate($value, $key)) {
            return false;
        }
    }
    return true;
}

/**
 * Cleanup CRLF new line endings.
 */
function normalize_line_endings(string $string): string
{
    return str_replace(["\r\n", "\r"], "\n", $string);
}

/**
 * Expand leading tilde (~) symbol in given path.
 */
function parse_home_dir(string $path): string
{
    if ('~' === $path || str_starts_with($path, '~/')) {
        if (isset($_SERVER['HOME'])) {
            $home = $_SERVER['HOME'];
        } elseif (isset($_SERVER['HOMEDRIVE'], $_SERVER['HOMEPATH'])) {
            $home = $_SERVER['HOMEDRIVE'] . $_SERVER['HOMEPATH'];
        } else {
            return $path;
        }

        return $home . substr($path, 1);
    }

    return $path;
}

function find_line_number(string $source, string $string): int
{
    $string = explode(PHP_EOL, $string)[0];
    $before = strstr($source, $string, true);
    if (false !== $before) {
        return count(explode(PHP_EOL, $before));
    }
    return 1;
}

function colorize_host(string $alias): string
{
    if (defined('NO_ANSI')) {
        return $alias;
    }

    if (in_array($alias, ['localhost', 'local'], true)) {
        return $alias;
    }

    if (getenv('COLORTERM') === 'truecolor') {
        $hsv = function ($h, $s, $v) {
            $r = $g = $b = $i = $f = $p = $q = $t = 0;
            $i = floor($h * 6);
            $f = $h * 6 - $i;
            $p = $v * (1 - $s);
            $q = $v * (1 - $f * $s);
            $t = $v * (1 - (1 - $f) * $s);
            switch ($i % 6) {
                case 0:
                    $r = $v;
                    $g = $t;
                    $b = $p;
                    break;
                case 1:
                    $r = $q;
                    $g = $v;
                    $b = $p;
                    break;
                case 2:
                    $r = $p;
                    $g = $v;
                    $b = $t;
                    break;
                case 3:
                    $r = $p;
                    $g = $q;
                    $b = $v;
                    break;
                case 4:
                    $r = $t;
                    $g = $p;
                    $b = $v;
                    break;
                case 5:
                    $r = $v;
                    $g = $p;
                    $b = $q;
                    break;
            }
            $r = round($r * 255);
            $g = round($g * 255);
            $b = round($b * 255);
            return "\x1b[38;2;{$r};{$g};{$b}m";
        };
        $total = 100;
        $colors = [];
        for ($i = 0; $i < $total; $i++) {
            $colors[] = $hsv($i / $total, .5, .9);
        }
        if ($alias === 'prod' || $alias === 'production') {
            return "$colors[99]$alias\x1b[0m";
        }
        if ($alias === 'beta') {
            return "$colors[14]$alias\x1b[0m";
        }
        $tag = $colors[abs(crc32($alias)) % count($colors)];
        return "$tag$alias\x1b[0m";
    }

    $colors = [
        'fg=cyan;options=bold',
        'fg=green;options=bold',
        'fg=yellow;options=bold',
        'fg=cyan',
        'fg=blue',
        'fg=yellow',
        'fg=magenta',
        'fg=blue;options=bold',
        'fg=green',
        'fg=magenta;options=bold',
        'fg=red;options=bold',
    ];
    $tag = $colors[abs(crc32($alias)) % count($colors)];
    return "<$tag>$alias</>";
}

function escape_shell_argument(string $argument): string
{
    return "'" . str_replace("'", "'\\''", $argument) . "'";
}

function deployer_root(): string
{
    if (getenv('DEPLOYER_ROOT') !== false) {
        return getenv('DEPLOYER_ROOT');
    } else {
        if (defined('DEPLOYER_DEPLOY_FILE')) {
            return dirname(DEPLOYER_DEPLOY_FILE);
        } else {
            return getcwd();
        }
    }
}
